package org.sdp.pearproject.controller;

import org.sdp.pearproject.config.*;
import org.sdp.pearproject.dao.domain.Member;
import org.sdp.pearproject.dao.mapper.MemberMapper;
import org.sdp.pearproject.dto.request.*;
import com.alibaba.fastjson.JSONObject;
import org.sdp.pearproject.utils.JWTUtil;
import org.sdp.pearproject.utils.JavaBeansUtil;
import org.springframework.beans.CachedIntrospectionResults;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.beans.PropertyDescriptor;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class BaseController {

    @Autowired
    ThreadLocalResponse threadLocalResponse;

    @Autowired
    MemberMapper memberMapper;

    @Autowired
    HttpServletRequest httpServletRequest;

    @Autowired
    HttpServletResponse httpServletResponse2;

    @Autowired
    ApplicationContext applicationContext;

    public static ConcurrentHashMap<Class, Date> doneIntrospectionResultsMap = new ConcurrentHashMap();

    public static final Map<String, Map<String, Class>> requestClassMap = new HashMap<>();
    public static final Map<String, Class> classMap = new HashMap<>();

    public static final Map<String, String> beanMap = new HashMap<>();

    public static final Map<String, Method> urlMap = new HashMap<>();

    private Map<Date, Set<String>> globalIdMap = new HashMap<>();

    public static void addRequestClass(String moduleName, String methodName, String beanName, Class clz, Class requestClz) {
        String key = moduleName + "/"+methodName;
        synchronized (BaseController.class) {
            if (!requestClassMap.containsKey(moduleName)) {
                requestClassMap.put(moduleName, new HashMap<>());
            }
            if (!requestClassMap.get(moduleName).containsKey(methodName)) {
                requestClassMap.get(moduleName).put(methodName, requestClz);
            }
            if (!classMap.containsKey(key)) {
                classMap.put(key, clz);
                beanMap.put(key, beanName);

                Method[] methods = clz.getDeclaredMethods();
                for(Method method: methods) {
                    if (method.isAnnotationPresent(PostMapping.class)) {
                        PostMapping postMapping = method.getAnnotation(PostMapping.class);
                        method.setAccessible(true);
                        for(String url: postMapping.value()) {
                            urlMap.put(moduleName + url, method);
                        }
                    }
                }
            }
        }
    }
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(Date.class, new MyDatePropertyEditorSupport());
    }

    public boolean needTotal(String table, String method, Object request) throws Exception {
        Field field = JavaBeansUtil.getField(request.getClass(), "page_size");
        if (field != null && field.get(request) != null) {
            field.set(request, null);
            for(String name: new String[]{"page", "page_no", "query_options"}) {
                field = JavaBeansUtil.getField(request.getClass(), name);
                if (field != null && field.get(request) != null) {
                    if ("query_options".equals(name) && field.get(request) instanceof JSONObject) {
                        JSONObject jsonObject = (JSONObject) field.get(request);
                        for(String queryProp: new String[]{"limit", "count"}) {
                            if (jsonObject.containsKey(queryProp)) {
                                jsonObject.remove(queryProp);
                            }
                        }
                    } else {
                        field.set(request, null);
                    }
                }
            }
            return true;
        }
        return false;
    }

    public void setTotal(String table, String method, Object request, Integer total) throws Exception {
        for(String name: new String[]{"page_size", "page", "page_no", "query_options"}) {
            Field field = JavaBeansUtil.getField(request.getClass(), name);
            Field fieldOri = JavaBeansUtil.getField(request.getClass(), name + "_ori");
            if (field != null && fieldOri != null && fieldOri.get(request) != null) {
                if ("query_options".equals(name) && field.get(request) != null && field.get(request) instanceof JSONObject && fieldOri.get(request) != null && fieldOri.get(request) instanceof JSONObject) {
                    JSONObject jsonObject = (JSONObject) fieldOri.get(request);
                    JSONObject destJSONObject = (JSONObject) field.get(request);
                    for(String queryProp: new String[]{"limit", "count"}) {
                        if (jsonObject.containsKey(queryProp)) {
                            destJSONObject.put(queryProp, jsonObject.get(queryProp));
                        }
                    }
                } else {
                    field.set(request, fieldOri.get(request));
                }
            }
        }

        //一定有page_size
        if (request instanceof BaseRequest) {
            BaseRequest baseRequest = (BaseRequest) request;

            if (baseRequest.getPageNo() == null) {
                baseRequest.setPageNo(1);
            }
            if (baseRequest.getQueryOptions() == null) {
                baseRequest.setQueryOptions(new JSONObject());
            }
            baseRequest.getQueryOptions().put("limit", (baseRequest.getPageNo() - 1)* baseRequest.getPageSize());
            baseRequest.getQueryOptions().put("count", baseRequest.getPageSize());
        }

        threadLocalResponse.set("total", total);
    }

    public void fixRequest(String module, String method, Object request) throws Exception {
        fixRequest(module, method, request, null);
    }

    public void fixRequest(String table, String method, Object request, Integer callFrom) throws Exception {

        threadLocalResponse.set("is_pear", null);
        threadLocalResponse.set("code", null);
        threadLocalResponse.set("message", null);
        threadLocalResponse.set("page", null);
        threadLocalResponse.set("without_total", null);

        this.threadLocalResponse.set("is_pear", 1);

        boolean ignoreAuth = false;
        if ("project".equals(table) && "login".equals(method)) {
            ignoreAuth = true;
        }

        if ("project".equals(table)) {
            if ("index_system_config".equals(method)) {
                this.threadLocalResponse.set("code", 200);
                ignoreAuth = true;
            }
        } else if ("index".equals(table)) {
            if ("index_check_install".equals(method)) {
                this.threadLocalResponse.set("code", 200);
                ignoreAuth = true;
            }
        }

        if (ignoreAuth) {
            return;
        }

        //备份输入参数
        {
            String[] fields = new String[]{
                    "member_code",
                    "organization_code",
                    "page",
                    "page_size",
                    "page_no",
                    "query_options",
            };
            for(String name: fields) {
                Field field = JavaBeansUtil.getField(request.getClass(), name);
                Field fieldOri = JavaBeansUtil.getField(request.getClass(), name + "_ori");
                if ("query_options".equals(name) && field.get(request) != null) {
                    System.out.println("");
                }
                if (field != null && fieldOri != null) {
                    fieldOri.set(request, field.get(request));
                    JavaBeansUtil.copyProperties(field.get(request), fieldOri.get(request));
                }
            }
        }

        {
            Field field = JavaBeansUtil.getField(request.getClass(), "page_size");
            Integer withoutTotal = 0;
            if (field == null || field.get(request) == null) {
                withoutTotal = 1;
            }
            threadLocalResponse.set("without_total", withoutTotal);
            field = JavaBeansUtil.getField(request.getClass(), "page_no");
            if (withoutTotal == 0 && field != null) {
                threadLocalResponse.set("page", field.get(request));
            }
        }

        if (request instanceof BaseRequest) {
            BaseRequest baseRequest = (BaseRequest) request;
            baseRequest.checkParam();

            if (baseRequest.getPageSize() != null) {
                if (baseRequest.getPageSize() <= 0) {
                    throw new Exception("invalid page_size");
                }
                if (baseRequest.getPageNo() == null) {
                    baseRequest.setPageNo(1);
                }
                if (baseRequest.getPageNo() == null || baseRequest.getPageNo() <= 0) {
                    throw new Exception("invalid page_no");
                }
                if (baseRequest.getQueryOptions() == null) {
                    baseRequest.setQueryOptions(new JSONObject());
                }
                baseRequest.getQueryOptions().put("limit", (baseRequest.getPageNo() - 1)* baseRequest.getPageSize());
                baseRequest.getQueryOptions().put("count", baseRequest.getPageSize());
            }
        }

        fixRequestAuth(table, method, request, true);
    }

    private void fixRequestAuth(String moduleName, String methodName, Object request, boolean checkUserInfo) throws Exception {

        if (!checkUserInfo) {
            return;
        }
        if (request instanceof BaseRequest) {
            BaseRequest baseRequest = (BaseRequest)request;
            ThreadLocalResponse threadLocalResponse = applicationContext.getBean(ThreadLocalResponse.class);

            String Authorization = httpServletRequest.getHeader("Authorization");

            if (StringUtils.hasText(Authorization)) {
                String memberCode = JWTUtil.getValue(Authorization.replace("bearer ", ""), "code");
                if (!StringUtils.hasText(memberCode)) {
                    throw new Exception("miss member_code in Authorization");
                }
                Member member = memberMapper.selectByCode(memberCode);
                if (member == null) {
                    throw new Exception("miss member information");
                }
                if ("project".equals(moduleName) && "project_member_invite_member".equals(methodName)) {

                } else {
                    Field field = JavaBeansUtil.getField(request.getClass(), "member_code");
                    if (field != null) {
                        field.set(request, memberCode);
                    }
                    field = JavaBeansUtil.getField(request.getClass(), "member_code_auth");
                    if (field != null) {
                        field.set(request, memberCode);
                    }
                }
            }

            String organizationCode = httpServletRequest.getHeader("organizationCode");

            if (StringUtils.hasText(organizationCode)) {
                Field field = JavaBeansUtil.getField(request.getClass(), "organization_code");
                if (field != null) {
                    field.set(request, organizationCode);
                }
                field = JavaBeansUtil.getField(request.getClass(), "organization_code_auth");
                if (field != null) {
                    field.set(request, organizationCode);
                }

            }
        }
    }

    public String toJSONString(Object request) {
        return JavaBeansUtil.toJSONString(request);
    }

    public JSONObject parseObject(String request) {
        return JavaBeansUtil.parseObject(request);
    }

    public Object decrypt(String moduleName, String methodName, String strRequest, Class clz) throws Exception {
        throw new Exception("not implement");
    }

    public String encrypt(BaseRequest request, String data) throws Exception {
        throw new Exception("not implement");
    }

    public void clearPageInfo(BaseRequest request) {
        if(request.getQueryOptions() != null) {
            request.getQueryOptions().remove("count");
            request.getQueryOptions().remove("limit");
        }
        request.setPageNo(null);
        request.setPageSize(null);
    }

    public String exportExcel(Object request,
                              Collection list) throws Exception {
        throw new Exception("not implement");
    }

}
